Skip to content

v3.0.0 - #3

Merged
pQu4k3r merged 18 commits into
OwnerPlugins:mainfrom
pQu4k3r:develop
Jul 19, 2026
Merged

v3.0.0#3
pQu4k3r merged 18 commits into
OwnerPlugins:mainfrom
pQu4k3r:develop

Conversation

@pQu4k3r

@pQu4k3r pQu4k3r commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

LinuxsatPanel v3.0.0

✨ New Features

  • Global addon search (GREEN button) — on the main panel, searches the entire
    catalog across every category and shows the matches as a normal installable list.
    On category/script grids, GREEN jumps straight to the first matching tile,
    switching page automatically. New "Search" label + green button bar in all
    three skins (HD / FHD / WQHD).
  • Installed addons highlighted — addon lists show already-installed packages
    in green, computed from the opkg/dpkg database when the list opens.
  • Update check on panel start — runs in the background at startup and shows
    version, changelog and update instructions. (The check existed but had been
    disconnected; it is now enabled, asynchronous, and uses a correct numeric
    version comparison.)

⚡ Performance — no more GUI freezes

  • All network work moved off the GUI thread via a new AsyncCall/AsyncMixin
    helper (background thread + eTimer delivery, auto-cancelled on screen close):
    • Addon catalog fetch (was blocking at panel open and on every category click)
    • Channel-list provider page scraping (Ciefp, Cyrus, Manutek, Vhannibal, …)
    • Package downloads before install
    • The whole settings install (download / unzip / copy)
    • System information collection (public IP lookups, connectivity probes)
  • Catalog cached per session (5-minute TTL) and pre-warmed in the background
    at panel open — category clicks are instant.
  • Offline boxes no longer wait ~20 s of network timeouts when opening the
    Information screen; public-IP lookups are skipped entirely when offline.

🛡️ Data safety

  • Settings install can no longer wipe your channel list. New flow:
    download → verify (unzip exit code + archive must actually contain a
    lamedb/.tv payload; both zip layouts supported) → automatic backup of
    /etc/enigma2 → wipe → install → automatic restore if the copy fails.
    Every failure path shows a clear message and leaves the box untouched.

🐛 Bug fixes

  • Grid navigation: UP barely worked (it jumped to the last page from most
    positions, and on page 2 it snapped to the first tile); LEFT wrapped to the
    wrong tile. All four arrows rewritten: UP/DOWN move one row and keep the
    column across pages, LEFT/RIGHT wrap cleanly page-to-page.
  • Partial grid pages: a last page with e.g. 14 entries showed 6 leftover
    placeholder icons; unused tiles are now hidden.
  • Yes/No dialogs: LCN scan and skin check ran even when answering No;
    LcnXX also reopened with the wrong callback.
  • Skin check crash: an eTimer was connected to the return value (None) of
    the already-executed check → guaranteed TypeError.
  • Update check: versions were compared as strings (2.9.x would never see
    2.10.x; 2.10.x was offered a "downgrade"); the changelog was never parsed;
    an empty response crashed callers. All fixed (version_tuple).
  • Free C-line fetcher: a missing comma merged two regex patterns into one
    broken pattern; a dead server raised instead of showing a message; duplicate
    server entries could be appended (missing break).
  • Package removal: filenames without _ lost their last character, so
    opkg remove targeted a nonexistent package.
  • Grid paging: item counts that are exact multiples of 20 produced a
    phantom empty page.
  • LCNScanner: the terrestrial bouquet restore after a settings install was
    a no-op (function defined but never called); bouquets.tv dedup compared a
    full path against name-only lines, appending a duplicate reference on every
    run; #NAME rename could never match; self-import in the ServiceScan hook.
  • Python 3 crashes: gzip catalog responses (StringIO fed bytes),
    b64decoder padding (bytes + str), undefined PY3 in fetch_url on py2.
  • Information screen: missing values printed a bare "Unknown" with no
    field label (operator-precedence bug).
  • View-log prompt: the eTimer opening File_Commander was a local variable
    and could be garbage-collected before firing — log viewer silently never
    opened. Timer now held on the screen instance.
  • Network requests without timeouts (script updater, package fetch) could hang
    the GUI forever.

🧹 Code quality & cleanup

  • Grid engine unified: six screens carried byte-identical copies of the
    20-tile grid machinery (~1,500 duplicated lines). It now lives once in
    LPGridScreen; plugin.py shrinks from ~5,100 to ~3,900 lines. Every future
    fix in this area lands once instead of six times.
  • Removed dead translate_utils.py (imported by nothing and unimportable) and
    two byte-identical duplicate sh/ scripts.
  • Removed dead keyNumberGlobal (referenced a widget that doesn't exist).
  • __pycache__/*.pyc added to .gitignore.

🔧 CI / Housekeeping

  • Lint (ruff, pylint, autopep8) and the translation updater now run on
    develop too; the auto-commit workflows push back to the triggering branch
    instead of hardcoded main. Autotag remains main-only (tags = releases).
  • Version bumped to 3.0.0 in __init__.py, CONTROL/control,
    installer.sh (update-check changelog included) and the README badge.
    Merging this PR will trigger the automatic tag + release.

pQu4k3r added 18 commits July 18, 2026 22:42
- check_version: the changelog branch was nested inside the version
  branch and never ran; versions were compared as strings so 2.9.x
  would never see a 2.10.x update; an empty response returned a bare
  None instead of a 3-tuple. Add version_tuple() and compare
  numerically.
- checkGZIP: gzip-encoded responses crashed on Python 3 because
  StringIO was fed bytes; use BytesIO.
- b64decoder: the padding fixup appended bytes to a str (TypeError on
  Python 3).
- fetch_url: PY3 was undefined when running on Python 2.
The Lcn, LcnXX and Checkskin prompts used 'if answer is None / else',
so answering No fell into the else branch and ran the scan or check
anyway. Guard with 'elif answer:'. LcnXX also reopened its prompt with
self.Lcn as callback, so the answer ran the wrong handler. Checkskin
additionally connected its eTimer to the return value of
check_module_skin() - the function was already executed and returns
None, so the timer fired into a TypeError; run the check directly and
drop the timer.
- A missing comma in the generic pattern list concatenated two regexes
  into one broken pattern that could never match.
- A dead server made make_request return None and '"x" in None' raise
  a confusing TypeError; fail cleanly with a clear message instead.
- The pattern loop kept scanning after a successful match, so a page
  matching several patterns appended duplicate server entries to the
  config; stop after the first hit.
- removenow: package filenames without an underscore lost their last
  character (ipk[:-1]), so opkg remove targeted a nonexistent package;
  strip the extension instead.
- The grid page count used npics // 20 + 1, creating a phantom empty
  page whenever an item count is an exact multiple of 20 (all six grid
  screens share the formula).
- requests.get in runScriptWithConsole and retfile had no timeout and
  could freeze the GUI thread forever on a stalled connection.
check_vers compared version strings lexicographically, so an installed
2.10.x would be offered a downgrade to 2.9.x and 2.9.x would never see
a 2.10.x update. Use the shared version_tuple helper.
'Label: %s' % x if x else 'Unknown' binds as ('Label: %s' % x) if x
else 'Unknown', so any missing value printed a bare 'Unknown' line
without its field label. Format through a small helper instead, and
show the real boolean for the VTi/DMM image flags (False previously
displayed as 'Unknown').
- TransferBouquetTerrestrialFinal defined its inner RestoreTerrestrial
  function but never called it, so the terrestrial bouquet restore
  after a settings install was a no-op.
- addInTVBouquets compared the bouquet's full path against bouquets.tv
  lines that only contain the file name, so the already-present check
  never matched and a duplicate reference (to a possibly different
  hardcoded name) was appended on every run; compare and write the
  actual bouquet file name.
- RestoreTerrestrial checked for '#NAME' inside line.lower(), which can
  never match, so the bouquet was never renamed to Digitale Terrestre.
- Terrestrial.py's ServiceScan hook imported itself through
  .LCNScanner.Terrestrial, a path that does not exist from inside the
  LCNScanner package; the class is defined in the same module.
translate_utils.py is imported by nothing and cannot be imported at
all: it expects DEBUG, HEADERS and SYSTEM_DIR from the package, which
do not exist (leftover from the ForecaOne plugin). The translation
workflow uses its own copies in update_translations.py.

bissfeedautokey.sh and ipaudiopro_1.4.sh are byte-identical duplicates
of Bissfeedautokey.sh and Ipaudiopro_1.4.sh; the capitalized names are
the ones referenced by plugin.py. Multistalker_pro.sh and
multisalker_pro1_eliesat.sh differ in content and stay untouched.
Opening the Information screen imported stbinfo on the GUI thread; its
module-level StbInfo() runs pings, TCP probes and up to four public-IP
lookups, blocking the whole interface for 20+ seconds on first open
(worse when offline, since every lookup runs into its timeout).

- LSinfo now shows 'Collecting system information...' immediately,
  gathers the data in a daemon thread (including the heavy first
  import) and applies the text from an eTimer on the main thread. The
  poll stops on screen close so a late result cannot touch a dead
  widget.
- stbinfo tracks connectivity as a boolean and skips the public-IP
  lookups entirely when the box is offline - previously the offline
  case was the slowest. Lookup timeouts trimmed to 3s.
okRun1 wiped lamedb and every .tv/.radio file from /etc/enigma2 before
checking that the download or unzip had succeeded: urlretrieve had no
error handling, the unzip exit code was ignored, and a zip with an
unexpected layout made the cp copy nothing - any of these left the box
with no channel list at all.

The install now runs download -> verify -> backup -> wipe -> install:
- download via requests with a timeout and clear failure message,
  aborting before /etc/enigma2 is touched;
- unzip exit code checked; the payload must actually contain a lamedb
  or .tv file (channel lists at the zip root are now handled too, and
  the top-level folder is detected instead of walking into the deepest
  subdirectory);
- the current /etc/enigma2 is saved to /tmp/settings_backup.tar.gz
  before the wipe and restored automatically if the copy fails.
Six screens (LinuxsatPanel, LSskin, LSChannel, LulullaScript,
CiefpInstaller, ScriptInstaller) each carried a byte-identical copy of
the 20-tile grid machinery: title/skin/resolution setup, widget and
ActionMap wiring, paging (openTest/paintFrame), navigation
(key_left/right/up/down), sorting and the view-log prompt. Every fix
in this area had to be applied six times.

The engine now lives once in an LPGridScreen base class: subclasses
fill their menu lists and call initGrid(). Behavior is unchanged,
verified by driving every screen through full navigation wrap-around,
paging and sort/restore in a stubbed enigma2 environment.

Also folded in while moving the code:
- _view_log kept its eTimer in a local variable, so the timer could be
  garbage collected before firing and the log viewer silently never
  opened; the timer is now held on the screen instance.
- Dropped keyNumberGlobal, dead code referencing a "menu" widget that
  none of these screens define, with no key binding pointing at it.

plugin.py shrinks by ~1150 lines net.
Every remaining network operation ran on the enigma main thread and
froze the whole GUI: the addon catalog was fetched at panel open AND
again on every category click, provider pages were scraped
synchronously when a channel-list screen opened, package downloads
blocked until complete, and the settings install (download + unzip +
copy) locked the interface for its entire duration.

- New AsyncCall helper runs a blocking function in a daemon thread and
  delivers the result to a callback on the main thread via eTimer.
  The AsyncMixin (used by LPGridScreen and addInstall) cancels pending
  callbacks when the screen closes, so a late result never touches a
  dead widget.
- The catalog is fetched once per session (5-minute TTL) and warmed in
  the background when the panel opens, so a category click is served
  from cache instantly; the shared _openCategory lives in LPGridScreen.
- Provider page scraping, package downloads and the settings install
  now show a status text and run in the background; the console or
  result message appears when the work is done.

Verified in the stubbed enigma2 environment end-to-end: cache hit
counting, panel prefetch, category click -> addInstall slice, provider
page failure path, async package install -> opkg console command, and
a full settings install with a real zip archive through the real shell
(unzip, payload check, backup, wipe, install), plus grid navigation
regression on the refactored screens.
- Search (GREEN button, shown in the skins next to Exit): on the main
  panel it searches the entire addon catalog across every category and
  opens the matches as a normal installable list; on the category
  grids it jumps straight to the first matching tile, changing page if
  needed. Runs through the async helper, so a cold catalog never
  blocks the GUI.
- Installed addons are shown in green in the addon lists. The
  installed-package scan that message() already did on click is now a
  shared helper (get_installed_packages) and feeds one flag per list
  entry.
- The startup update check, present but disconnected since its
  onLayoutFinish line was commented out, is enabled again: it runs in
  the background at panel open and shows a single clear message
  (version, changelog, how to update) instead of the old
  modal-detection logic. Uses the fixed numeric version comparison.
- AsyncCall hardened against double delivery of a result.

Verified in the stubbed environment: update prompt on a newer remote
version, global search returning only matching catalog entries plus
the no-match path, grid search jumping to the correct tile and page,
installed-flag computation against the package database, and list
entry rendering with the installed color. All three LinuxsatPanel.xml
skins extended with the key_green label and re-validated as XML.
Lint (ruff, pylint, autopep8) and the translation updater only
triggered on main, so develop work got no checks until release. The
two auto-commit workflows also hardcoded 'origin main' in their
pull/push steps; they now use the triggering branch, and the autopep8
commit step is limited to push events so pull requests are lint-only.
Autotag stays main-only since tags mark releases.
Version bump across __init__.py, CONTROL/control, installer.sh and the
README badge. The installer.sh changelog (what the in-plugin update
check displays) summarizes the 3.0.0 line:

- Search all addons with the GREEN button
- Installed addons shown in green in the lists
- No more GUI freezes: all network work is asynchronous and the addon
  catalog is cached per session
- Settings install verifies the download and keeps an automatic backup
- Update check runs at panel start
- Grid engine unified in one base class, dead code removed and a long
  list of bug fixes (see the individual commits)
key_up's conditions almost never matched a real cursor state: pressing
UP anywhere but a page's first tile fell into a branch that jumped to
the last page, and because openTest() resets the index to the page
start, on page 2 the cursor just snapped to the first tile instead of
moving up a row. key_left's wrap had the same openTest problem, landing
on the first tile of the previous page instead of the last one.

All four direction keys are rewritten with plain row/column logic:
LEFT/RIGHT step one tile and wrap page-to-page at the edges; UP/DOWN
move one row and cross pages keeping the current column (clamped on a
partial last row).

Verified exhaustively in the stubbed environment: every key from every
position on grids of 6, 16, 20, 25, 37, 40 and 45 items keeps the
cursor inside the visible page, full LEFT/RIGHT cycles visit every
tile exactly once and wrap home, UP/DOWN are inverse operations within
a page, and the reported case (UP on page 2) moves one row up.
The last page pre-filled all 20 slots with the LSS.png placeholder and
then overwrote only the occupied ones, so a page with e.g. 14 entries
showed 6 leftover placeholder icons. openTest now draws the occupied
tiles and hides the unused pixmaps (re-showing them when a full page
is displayed again); the min/max entry computation for full and last
pages is unified in the process.

Verified for 6/16/20/25/34/37/45-item grids: the last page shows
exactly its own tiles, returning to a full page restores all 20, and
the navigation invariants still hold from every position.
The import inside check_and_install_requests exists only to test
whether the module is installed before falling back to opkg/apt-get,
so the unused-import warning is intentional. importlib.util.find_spec
is not an option while the Python 2 code paths remain.
@pQu4k3r
pQu4k3r merged commit 7e392fd into OwnerPlugins:main Jul 19, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant